package jpsearch;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javafx.scene.control.TreeItem;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import jpsearchcore.ContentDesciption;
import jpsearchcore.ContentDesciption.CDEvent;
import jpsearchcore.ContentDesciption.CDObject;
import jpsearchcore.ContentDesciption.CDOrganizationType;
import jpsearchcore.ContentDesciption.CDPerson;
import jpsearchcore.ExternalDescription;
import jpsearchcore.ExternalDescription.EDLiteralValue;
import jpsearchcore.ExternalDescription.EDResourceValue;
import jpsearchcore.ExternalDescription.EDStructuredValue;
import jpsearchcore.ExternalDescription.EDTagName;
import jpsearchcore.GPSPositioning;
import jpsearchcore.JPSearchCore;
import jpsearchcore.OriginalImageIdentifier;
import jpsearchcore.Person;
import jpsearchcore.Place;
import jpsearchcore.Publisher;
import jpsearchcore.Rating;
import jpsearchcore.RegionLocator;
import jpsearchcore.RegionLocator.Region;
import jpsearchcore.RegionOfInterest;
import jpsearchcore.RightsDescription;
import jpsearchcore.Source;
import jpsearchcore.SourceElement;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Attribute;
import org.jdom2.DataConversionException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import translation.FilteredSourceFieldType;
import translation.FilteredSourceFieldType.VariableBinding;
import translation.FormattedTargetFieldType;
import translation.ManyToOne;
import translation.ManyToOneFieldTranslationType;
import translation.OneToMany;
import translation.OneToManyFieldTranslationType;
import translation.OneToOne;
import translation.OneToOneFieldTranslationType;
import translation.SourceFieldType;
import translation.SourceType;
import translation.TargetFieldType;
import translation.TargetType;
import translation.TranslationRuleType;
import translation.TranslationRulesType;
import translation.VarBinding;

@SuppressWarnings("rawtypes")
public class JPSearchTranslator {

	final public static int GET_LAST_ELEMENT = -2;
	final public static int NORMAL_TOKEN_PARSER = 77;
	final public static int VARIABLE_BINDING_TOKEN_PARSER = 78;

	static JPSearchCore jpsC = null;
	static Map<String, Object> rules = new HashMap<String, Object>();

	/**
	 * Translates an XML instance file accompanied with its Rule XML file 
	 * into a JPSearchCore object
	 * @param inputXml The XML instance file
	 * @param inputXMLRules The XML Rule file
	 * @param unknownElements The map for the unmapped elements to be added
	 * @return The JPSearchCore object filled with the translated information
	 * @throws Exception
	 */
	public static JPSearchCore translateToJPSCore(File inputXml, File inputXMLRules, Map<String, String> unknownElements) throws Exception{
		jpsC = new JPSearchCore();
		parseRuleXmlFile(inputXMLRules);
		return parseAndTranslateXmlFile(inputXml, unknownElements);			
	}

	/**
	 * Dumbs the Rules in a readable format, to a specified file
	 * @param inputXMLRules
	 * @param outputFile
	 * @throws IOException 
	 * @throws JAXBException 
	 */
	public static void dumpRulesToFile(File inputXMLRules, String outputFile) throws JAXBException, IOException{
		parseRuleXmlFile(inputXMLRules); //fill rule map structure

		if(rules.isEmpty())
			return;

		File output = new File(outputFile);

		if (!output.exists()) {
			output.createNewFile();
		}

		FileWriter fw = new FileWriter(output.getAbsoluteFile());
		BufferedWriter bw = new BufferedWriter(fw);

		bw.write("FromXPath FromRegex ExplicitPrefix ExplicitPostFix ListBinding ExplicitBinding ToXPaths ToRegex\n");

		Iterator<Map.Entry<String, Object>> it = rules.entrySet().iterator();
		while (it.hasNext()) {	
			Map.Entry<String, Object> rule = it.next();
			//String key = rule.getKey();
			Object value = rule.getValue();

			if(value instanceof OneToOne){
				OneToOne oto = (OneToOne)value;	

				bw.write(oto.getFromXPath());				
				if(oto.getFromRegEx() != null)
					bw.write(" " + oto.getFromRegEx());

				bw.write(" ");

				bw.write(oto.getToXPath());
				if(oto.getToRegEx() != null)
					bw.write(" " + oto.getToRegEx());

				if( oto.getVariableBinding() != null){
					for(VarBinding vb : oto.getVariableBinding()){
						if(vb.getExplicitPrefixBinding() != null && 
								vb.getExplicitPrefixBinding().isEmpty() == false)
							bw.write(" " + vb.getExplicitPrefixBinding().get(0));

						if(vb.getExplicitPostfixBinding() != null && 
								vb.getExplicitPostfixBinding().isEmpty() == false)
							bw.write(" " + vb.getExplicitPostfixBinding().get(0));

						if(vb.getListBinding() != null)
							bw.write(" " + vb.getListBinding());

						if(vb.getExplicitBinding() != null)
							bw.write(" " + vb.getExplicitBinding());
					}
				}
				bw.write("\n");
			}
			else if(value instanceof OneToMany){
				OneToMany otm = (OneToMany)value;
			}
			else if(value instanceof ManyToOne){
				ManyToOne mto = (ManyToOne)value;
			}
		}

		bw.close();		
	}

	/**
	 * Parses the XML Rule file and fills the mapo structure with the rules
	 * @param inputXMLRules The XML rule file
	 * @throws JAXBException
	 * @throws IOException
	 */
	private static void parseRuleXmlFile(File inputXMLRules) throws JAXBException, IOException{
		try {
			JAXBContext jc = JAXBContext.newInstance("translation");
			Unmarshaller u = jc.createUnmarshaller();
			JAXBElement element = (JAXBElement)u.unmarshal(new FileReader(inputXMLRules));

			// Parsing Rules file - fill rules structure using the additional classes
			TranslationRulesType root = (TranslationRulesType) element.getValue();
			for(TranslationRuleType trt: root.getTranslationRule()){

				//Translation Rule: OneToOne FieldTranslationType
				if(trt instanceof OneToOneFieldTranslationType){
					OneToOneFieldTranslationType o = (OneToOneFieldTranslationType)trt;

					//FilteredSourceFieldType
					if(o.getFromField() instanceof FilteredSourceFieldType){ 
						FilteredSourceFieldType sft = (FilteredSourceFieldType)o.getFromField();
						String fromXPath = sft.getXPathExpression();
						String fromRegex = sft.getFilterWithRegExpr();						
						String toXPath = null;
						String toRegex = null;

						//FormattedTargetFieldType
						if(o.getToField() instanceof FormattedTargetFieldType){
							toXPath = ((FormattedTargetFieldType)o.getToField()).getXPathExpression();	
							toRegex = ((FormattedTargetFieldType)o.getToField()).getReplaceWithRegExpr();	
						}
						else{
							toXPath = ((TargetFieldType)o.getToField()).getXPathExpression();	
						}

						OneToOne oto = new OneToOne(fromXPath, fromRegex, toXPath, toRegex);

						//Variable Binding
						if(sft.getVariableBinding() != null && sft.getVariableBinding().size() > 0){							
							List<VarBinding> vbList = oto.initVariableBinding();
							for(VariableBinding vb : sft.getVariableBinding()){
								VarBinding newVB = new VarBinding();
								newVB.setExplicitBinding(vb.getExplicitBinding());
								newVB.setListBinding(vb.getListBinding());
								newVB.addAllExplicitPostfixBinding(vb.getExplicitPostfixBinding());								
								newVB.addAllExplicitPrefixBinding(vb.getExplicitPrefixBinding());	
								vbList.add(newVB);
							}
						}					

						rules.put(fromXPath, oto);
					}
					else{
						//Just SourceFieldType (Only XPathExpression)
						SourceFieldType sft = (SourceFieldType)o.getFromField();
						String fromXPath = sft.getXPathExpression();
						String toXPath = null;
						//if(o.getToField() instanceof TargetFieldType)
						toXPath = ((TargetFieldType)o.getToField()).getXPathExpression();	
						rules.put(fromXPath, new OneToOne(fromXPath, toXPath));
					}					
				}

				//Translation Rule: OneToMany FieldTranslationType
				else if(trt instanceof OneToManyFieldTranslationType){

					OneToManyFieldTranslationType o = (OneToManyFieldTranslationType)trt;

					//FilteredSourceFieldType
					if(o.getFromField() instanceof FilteredSourceFieldType){

						FilteredSourceFieldType f = (FilteredSourceFieldType)o.getFromField();							
						OneToMany otm = new OneToMany(f.getXPathExpression(), f.getFilterWithRegExpr());

						//Variable Binding
						if(f.getVariableBinding() != null && f.getVariableBinding().size() > 0){							
							List<VarBinding> vbList = otm.initVariableBinding();
							for(VariableBinding vb : f.getVariableBinding()){
								VarBinding newVB = new VarBinding();
								newVB.setExplicitBinding(vb.getExplicitBinding());
								newVB.setListBinding(vb.getListBinding());
								newVB.addAllExplicitPostfixBinding(vb.getExplicitPostfixBinding());								
								newVB.addAllExplicitPrefixBinding(vb.getExplicitPrefixBinding());	
								vbList.add(newVB);
							}
						}	

						List<TargetType> to = o.getToField();
						for(TargetType tt : to){
							String toXPath = null;
							String toRegEx = null;

							//FormattedTargetFieldType
							if(tt instanceof FormattedTargetFieldType){
								toXPath = ((FormattedTargetFieldType)tt).getXPathExpression();
								toRegEx = ((FormattedTargetFieldType)tt).getReplaceWithRegExpr();								
							}
							else //TargetFieldType		
								toXPath = ((TargetFieldType)tt).getXPathExpression();								

							otm.getToFields().put(toXPath, toRegEx);
						}

						rules.put(f.getXPathExpression(), otm);					
					}
					else{
						//Just SourceFieldType (Only XPathExpression) ???
						//SourceFieldType sft = (SourceFieldType)o.getFromField();
						//String fromXPath = sft.getXPathExpression();
					}
				}	

				//Translation Rule: ManyToOne FieldTranslationType
				else if(trt instanceof ManyToOneFieldTranslationType){
					ManyToOneFieldTranslationType m = (ManyToOneFieldTranslationType)trt;

					//FormattedTargetFieldType FOR SURE
					FormattedTargetFieldType ftt = (FormattedTargetFieldType)m.getToField();
					ManyToOne mto = new ManyToOne(ftt.getXPathExpression(), ftt.getReplaceWithRegExpr());						

					List<SourceType> from = m.getFromField();				

					int i = 1;
					for(SourceType st : from){
						if(st instanceof FilteredSourceFieldType){ //FilterWithRegEx exists
							FilteredSourceFieldType fsft = (FilteredSourceFieldType)st;
							String xPathExpression = fsft.getXPathExpression();
							mto.getFromXPath().put(xPathExpression, null);
							mto.getFromRegEx().put(xPathExpression, fsft.getFilterWithRegExpr());

							//Variable Binding - Found in FilteredSourceFieldType
							if(fsft.getVariableBinding() != null && fsft.getVariableBinding().size() > 0){							
								List<VarBinding> vbList = new ArrayList<VarBinding>();
								for(VariableBinding vb : fsft.getVariableBinding()){
									VarBinding newVB = new VarBinding();
									newVB.setExplicitBinding(vb.getExplicitBinding());
									newVB.setListBinding(vb.getListBinding());
									newVB.addAllExplicitPostfixBinding(vb.getExplicitPostfixBinding());								
									newVB.addAllExplicitPrefixBinding(vb.getExplicitPrefixBinding());	
									vbList.add(newVB);
								}
								mto.getVariableBinding().put(xPathExpression, vbList);
							}	
						}
						else{						
							SourceFieldType sft = (SourceFieldType)st;
							mto.getFromXPath().put(sft.getXPathExpression(), "$"+i);
							i++;
						}
					}				
					String firstPath = ((FilteredSourceFieldType)from.get(0)).getXPathExpression();

					rules.put(firstPath, mto);
				}						
			}
		}
		catch( JAXBException e){
			System.out.println("Error reading XML file. Check XML syntax");
			e.printStackTrace();
			throw e;
		}
		catch(Exception e){
			e.printStackTrace();
			throw e;
		}
	}

	/**
	 * Parses and translates the XML instance file having in mind that the 
	 * rule have already been read.
	 * @param inputXml XML instance file
	 * @param unknownElements The map for the unmapped elements to be added
	 * @return The JPSearch Core object filled with the translated metadata
	 * @throws JDOMException
	 * @throws IOException
	 */
	private static JPSearchCore parseAndTranslateXmlFile(File inputXml, Map<String, String> unknownElements) throws JDOMException, IOException{
		if(rules.isEmpty())		
			return null;

		SAXBuilder builder = new SAXBuilder();
		try {
			Document document = (Document) builder.build(inputXml);
			Element rootNode = document.getRootElement();
			List<Element> elements = rootNode.getChildren();
			List<String> mappedElements = new ArrayList<String>();

			for(Element e : elements){

				Map<String, String> paths = new LinkedHashMap<String, String>();
				Map<String, Integer> attibutes = new LinkedHashMap<String,Integer>();

				//The map that contains the paths is structured as {xpath, value}. eg. {"author/name", "John"}
				//The map that contains the id of ROI type ONLY, is structures as {xpath, id}. eg. {"region_of_interest/description", 0}
				pathCreator(e, paths, attibutes);

				//iterate all paths, for each check type
				Iterator<Map.Entry<String, String>> it = paths.entrySet().iterator();
				while (it.hasNext()) {	
					Map.Entry<String, String> pair = it.next();

					String xpath = pair.getKey();
					String value = pair.getValue();
					int roiElement = attibutes.get(xpath);

					/*
					 * OneToOne
					 */
					if(rules.get(xpath) instanceof OneToOne){
						OneToOne oto = (OneToOne)rules.get(xpath);
						String path[] = null;

						//Regular expressions included
						if(oto.getFromRegEx() != null && oto.getToRegEx() != null && oto.getVariableBinding() == null){
							Map<String, String> tokens = regexParser(oto.getFromRegEx(), value, 1, NORMAL_TOKEN_PARSER);
							String fValue = oto.getToRegEx();

							Iterator<Map.Entry<String, String>> tokensIt = tokens.entrySet().iterator();
							while (tokensIt.hasNext()) {
								Map.Entry<String, String> tempPair = (Entry<String, String>) tokensIt.next();

								String key = tempPair.getKey(); //eg. $1
								String kValue = tempPair.getValue(); //eg. 27 
								fValue = fValue.replace(key, kValue);					        
								tokensIt.remove(); // avoids a ConcurrentModificationException
							}		
							value = fValue;		
							path = oto.getToXPath().split("/");								
						}

						//SPECIAL CASE where explicit binding is used
						if(oto.getFromRegEx() == null && oto.getVariableBinding() != null){
							String fValue = oto.getToRegEx(); //ToRegex | eg. $1 or "Mr. $1"
							String toFieldKey = oto.getToXPath(); //ToXPath | eg. JPSearchCore/Modifiers/GivenName

							for(VarBinding vb : oto.getVariableBinding()){
								if(vb.getExplicitBinding() != null){
									if(fValue == null){
										if(toFieldKey.contains(vb.getExplicitBinding())){
											path = toFieldKey.split("/");
											path = ArrayUtils.removeElement(path, vb.getExplicitBinding()); //remove any variables
											fillCore(path, value, roiElement, jpsC);
											mappedElements.add(xpath);													
										}
									}
									else{	
										if(fValue.contains(vb.getExplicitBinding())){
											value = fValue.replace(vb.getExplicitBinding(), value);
											path = toFieldKey.split("/");
											fillCore(path, value, roiElement, jpsC);
											mappedElements.add(xpath);
										}
									}
								}
							}

							it.remove();
							continue;
						}

						//Variable Binding
						if(oto.getVariableBinding() != null ){									

							//First tokenize input
							Map<String, String> tokens = regexParser(oto.getFromRegEx(), value, 1, NORMAL_TOKEN_PARSER);
							Iterator<Map.Entry<String, String>> tokensIt = tokens.entrySet().iterator();
							Map.Entry<String, String> tempPair = (Entry<String, String>) tokensIt.next();

							String finalValueAllChanges = null;

							//DO THE MAPPINGS OF VARIABLES
							//i.e: {$a => name, $b => surname}							
							Map<String, String> variableBindings = new HashMap<String, String>();
							List<String> tokenValues = new ArrayList<String>(tokens.values());

							for(VarBinding v : oto.getVariableBinding()){

								//Prefix
								if(v.getExplicitPrefixBinding() != null){
									variableBindings.put(v.getExplicitPrefixBinding().get(0), tokenValues.get(0));
									tokenValues.remove(0);
								}

								//List Binding
								if(v.getListBinding() != null){
									for(String s : v.getListBinding().split(" ")){
										variableBindings.put(s, tokenValues.get(0));
										tokenValues.remove(0);
									}
								}

								//Postfix
								if(v.getExplicitPostfixBinding() != null){
									int lastIndex = tokenValues.size() - 1;
									variableBindings.put(v.getExplicitPostfixBinding().get(0), tokenValues.get(lastIndex));
								}
							}

							// ------- MAPPINGS END ------------------

							for(VarBinding v : oto.getVariableBinding()){
								//Explicit Prefix Binding - Has to be checked first then the rest (based on schema)
								//The first prefix binding in the list is taken (makes more sense)
								if(v.getExplicitPrefixBinding() != null){			

									//Then take the first token stored
									//check if the variable matches e.g. $a
									//Example: /JPSearchCore/Identifier/$a
									if(oto.getToXPath().contains(v.getExplicitPrefixBinding().get(0))){
										path = oto.getToXPath().split("/");
										path = ArrayUtils.removeElement(path, v.getExplicitPrefixBinding().get(0)); //remove any variables
										value = tempPair.getValue();
										tokensIt.remove();
									}		
									else if(oto.getToRegEx() != null &&
											oto.getToRegEx().contains(v.getExplicitPrefixBinding().get(0))){
										String reg = v.getExplicitPrefixBinding().get(0);
										if(finalValueAllChanges == null) 
											finalValueAllChanges = oto.getToRegEx();

										finalValueAllChanges = finalValueAllChanges.replace(reg, variableBindings.get(reg));

										if(!finalValueAllChanges.contains("$")){ //done with replacing
											path = oto.getToXPath().split("/");	
											value = finalValueAllChanges;
											break;
										}
									}
								}
								//ListBinding
								if(v.getListBinding() != null && oto.getToRegEx() != null){
									if(finalValueAllChanges == null) finalValueAllChanges = oto.getToRegEx();

									//Check current toField if it contains each regex
									for(String s : v.getListBinding().split(" ")){ //splits by space eg. [$a, $b]	
										//This is for regex that appear in toXPath
										if(finalValueAllChanges.contains(s)){
											finalValueAllChanges = finalValueAllChanges.replace(s, variableBindings.get(s));
											if(!finalValueAllChanges.contains("$")){ //done with replacing
												path = oto.getToXPath().split("/");	
												value = finalValueAllChanges;
												break;
											}
										}												
									}									
								}									

								//Postfix
								if(v.getExplicitPostfixBinding() != null){

									//check if the variable matches e.g. $a
									//Example: /JPSearchCore/Identifier/$a
									if(oto.getToXPath().contains(v.getExplicitPostfixBinding().get(0))){											
										value = variableBindings.get(v.getExplicitPostfixBinding().get(0));
										path = oto.getToXPath().split("/");
										path = ArrayUtils.removeElement(path, v.getExplicitPostfixBinding().get(0)); //remove any variables
									}

									else if(oto.getToRegEx() != null &&
											oto.getToRegEx().contains(v.getExplicitPostfixBinding().get(0))){
										String reg = v.getExplicitPostfixBinding().get(0);											
										if(finalValueAllChanges == null)
											finalValueAllChanges = oto.getToRegEx();

										finalValueAllChanges = finalValueAllChanges.replace(reg, variableBindings.get(reg));
										if(!finalValueAllChanges.contains("$")){ //done with replacing
											path = oto.getToXPath().split("/");	
											value = finalValueAllChanges;
											break;
										}
									}
								}
							}
						}
						else{
							path = oto.getToXPath().split("/");
						}

						fillCore(path, value, roiElement, jpsC);
						mappedElements.add(xpath);
						it.remove();							
					}

					/*
					 * OneToMany
					 */
					else if(rules.get(xpath) instanceof OneToMany){

						OneToMany otm = (OneToMany)rules.get(xpath);
						String path[] = null; //xpath broken for each iteration
						Iterator<Map.Entry<String, String>> otmIt = otm.getToFields().entrySet().iterator();

						//SPECIAL CASE where explicit binding is used
						if(otm.getFromRegEx() == null && otm.getVariableBinding() != null){
							while (otmIt.hasNext()) { //iterate through To Fields
								Map.Entry<String, String> tempPair = (Entry<String, String>) otmIt.next();
								String fValue = tempPair.getValue(); //ToRegex | eg. $1 or "Mr. $1"
								String toFieldKey = tempPair.getKey(); //ToXPath | eg. JPSearchCore/Modifiers/GivenName

								//No regex exists
								if(fValue == null){
									for(VarBinding vb : otm.getVariableBinding()){
										if(vb.getExplicitBinding() != null){
											if(toFieldKey.contains(vb.getExplicitBinding())){
												path = toFieldKey.split("/");
												path = ArrayUtils.removeElement(path, vb.getExplicitBinding()); //remove any variables
												fillCore(path, value, roiElement, jpsC);
												mappedElements.add(xpath);													
											}
										}
									}
								}
								else{	
									for(VarBinding vb : otm.getVariableBinding()){
										if(vb.getExplicitBinding() != null &&
												fValue.contains(vb.getExplicitBinding())){
											value = fValue.replace(vb.getExplicitBinding(), value);
											path = toFieldKey.split("/");
											fillCore(path, value, roiElement, jpsC);
											mappedElements.add(xpath);
										}
									}
								}

								it.remove();
								continue;
							}
						}

						Map<String, String> tokens = regexParser(otm.getFromRegEx(), value, 1, NORMAL_TOKEN_PARSER); //eg. $1 => "Nik", $2 => Dem
						Iterator<Map.Entry<String, String>> tokensIt = tokens.entrySet().iterator();

						//DO THE MAPPINGS OF VARIABLES
						//i.e: {$a => name, $b => surname}							
						Map<String, String> variableBindings = new HashMap<String, String>();
						List<String> tokenValues = new ArrayList<String>(tokens.values());

						if(otm.getVariableBinding() != null){
							for(VarBinding v : otm.getVariableBinding()){

								//Prefix
								if(v.getExplicitPrefixBinding() != null){
									variableBindings.put(v.getExplicitPrefixBinding().get(0), tokenValues.get(0));
									tokenValues.remove(0);
								}

								//List Binding
								if(v.getListBinding() != null){
									for(String s : v.getListBinding().split(" ")){
										variableBindings.put(s, tokenValues.get(0));
										tokenValues.remove(0);
									}
								}

								//Postfix
								//The first postfix binding in the list is taken (makes more sense)
								if(v.getExplicitPostfixBinding() != null){
									int lastIndex = tokenValues.size() - 1;
									variableBindings.put(v.getExplicitPostfixBinding().get(0), tokenValues.get(lastIndex));
								}
							}
						}
						// ------- MAPPINGS END ------------------

						//iterate through the "To" Fields of the object
						//for each one filter it through the variable binding types
						//eg. {/JPSearchCore/Modifiers/GivenName => $1, /JPSearchCore/Modifiers/FamilyName => $2}
						while (otmIt.hasNext()) {
							Map.Entry<String, String> tempPair = (Entry<String, String>) otmIt.next();
							String fValue = tempPair.getValue(); //ToRegex | eg. $1 or "Mr. $1"
							String toFieldKey = tempPair.getKey(); //ToXPath | eg. JPSearchCore/Modifiers/GivenName
							tokens = regexParser(otm.getFromRegEx(), value, 1, NORMAL_TOKEN_PARSER); //eg. $1 => "Nik", $2 => Dem
							tokensIt = tokens.entrySet().iterator();

							//ToRegex exists (non empty) and Variable binding is empty
							if(fValue != null && otm.getVariableBinding() == null){
								//Iterate through all tokens and replace if any exist in final value									
								while (tokensIt.hasNext()) {
									Map.Entry<String, String> tempTokenPair = (Entry<String, String>) tokensIt.next();
									String key = tempTokenPair.getKey(); //eg. $1
									if(fValue.contains(key)){ //if it exists in ToRegex
										String value2 = tempTokenPair.getValue();
										fValue = fValue.replace(key, value2);
									}
								}
								path = tempPair.getKey().split("/"); //eg. [JPSearchCore, Modifiers, GivenName]
								fillCore(path, fValue, roiElement, jpsC);
								otmIt.remove(); // avoids a ConcurrentModificationException
							}

							//ToXPath with variable only - check variable binding
							else if(otm.getVariableBinding() != null){ 
								List<VarBinding> vb = otm.getVariableBinding();
								String finalValueAllChanges = fValue; //As the value may contain various regexs it is kept
								//to iterate through all the VarBind types		

								for(VarBinding v : vb){

									//Prefix
									//The first prefix binding in the list is taken (makes more sense)
									if(v.getExplicitPrefixBinding() != null){ 			

										//Then take the first token stored
										//check if the variable matches e.g. $a
										//Example: /JPSearchCore/Identifier/$a
										if(toFieldKey.contains(v.getExplicitPrefixBinding().get(0))){
											path = toFieldKey.split("/");
											path = ArrayUtils.removeElement(path, v.getExplicitPrefixBinding().get(0)); //remove any variables												

											fillCore(path, fValue, roiElement, jpsC);
											otmIt.remove(); // avoids a ConcurrentModificationException
											break;
										}				

										//check if ToRegex exist e.g. "$a, $b"
										else if(finalValueAllChanges != null){
											String reg = v.getExplicitPrefixBinding().get(0);
											if(finalValueAllChanges.contains(reg)){ //if the regex contains the variable
												finalValueAllChanges = finalValueAllChanges.replace(reg, variableBindings.get(reg));

												if(!finalValueAllChanges.contains("$")){ //done with replacing
													path = toFieldKey.split("/");
													fillCore(path, finalValueAllChanges, roiElement, jpsC);
													break;
												}
											}
										}
									}

									//ListBinding
									if(v.getListBinding() != null){
										//Check current toField if it contains each regex
										for(String s : v.getListBinding().split(" ")){ //splits by space eg. [$a, $b]	
											//This is for regex that appear in toXPath
											if(toFieldKey.contains(s)){
												path = toFieldKey.split("/");
												path = ArrayUtils.removeElement(path, s); //remove any variables													

												fValue = variableBindings.get(s);													
												fillCore(path, fValue, roiElement, jpsC);
												break; 
											}	

											//check if ToRegex exist e.g. "$a, $b" AND if the regex contains the variable
											else if(finalValueAllChanges != null && finalValueAllChanges.contains(s)){
												finalValueAllChanges = finalValueAllChanges.replace(s, variableBindings.get(s));

												if(!finalValueAllChanges.contains("$")){ //done with replacing
													path = toFieldKey.split("/");
													fillCore(path, finalValueAllChanges, roiElement, jpsC);
													break;
												}
											}
										}									
									}

									//PostFix
									if(v.getExplicitPostfixBinding() != null){
										//check if the variable matches e.g. $a
										//Example: /JPSearchCore/Identifier/$a
										if(toFieldKey.contains(v.getExplicitPostfixBinding().get(0))){	
											fValue = variableBindings.get(v.getExplicitPostfixBinding().get(0));

											path = toFieldKey.split("/");
											path = ArrayUtils.removeElement(path, v.getExplicitPostfixBinding().get(0)); //remove any variables
											fillCore(path, fValue, roiElement, jpsC);
											otmIt.remove(); // avoids a ConcurrentModificationException
											break;
										}	

										//check if ToRegex exist e.g. "$a, $b"
										else if(finalValueAllChanges != null){
											String reg = v.getExplicitPostfixBinding().get(0);
											if(finalValueAllChanges.contains(reg)){ //if the regex contains the variable

												finalValueAllChanges = finalValueAllChanges.replace(reg, variableBindings.get(reg));
												if(!finalValueAllChanges.contains("$")){ //done with replacing
													path = toFieldKey.split("/");
													fillCore(path, finalValueAllChanges, roiElement, jpsC);
													break;
												}
											}
										}
									}
								}
							}			
						}	
						it.remove();
						mappedElements.add(xpath);
					}			

					/*
					 * ManyToOne
					 * 
					 * Supports different nested elements e.g. dob and time:
					 * <author>
								<author_name elem="0">Nicolas</author_name>
								<author_surname elem="0">Demetriou</author_surname>
								<dateofbirth>17-09-1987</dateofbirth>
							</author>
							<time>12:15:59</time>

							What could be done is while reading the XML and encounter the first element that
							belongs to a ManyToOne rule, search for the rest of the "FromFields" stored in 
							that rule. At the same time have a List to keep the already processed xml elements
							so that when the iteration continues, the current element is discarded.

					 * In the case of ManyToOne, several elements having a common path 
					 * may exist so this is checked by the second element before the last
					 * eg. author/dateofbirth/day => dateofbirth
					 * 							
					 */
					else if(rules.get(xpath) instanceof ManyToOne){

						/*
						 * For each from field in ManyToOne structure check if it exists in current paths element 
						 * (the one iterated by it) and if yes, add it to copy. If not, search for it in the whole
						 * XML instance (use the method provided); this means that it is in the case mention above
						 * with the different nested elements.
						 * 
						 */

						//Copy map contains the key-value pairs read from the XML instance e.g. [time, 12:12:12]
						Map<String,String> copy = new LinkedHashMap<String, String>();
						ManyToOne mto = (ManyToOne)rules.get(xpath);

						int itemSize = 0; //so to know the last item's id; i.e. in case of regex in from fields

						if (mto.getFromXPath().get(xpath) != null)
							itemSize++;

						Iterator<Map.Entry<String, String>> fromIt = mto.getFromXPath().entrySet().iterator();									

						while(fromIt.hasNext()){
							Map.Entry<String, String> fromPair = (Entry<String, String>) fromIt.next();			
							String fromPath = fromPair.getKey();

							if(paths.containsKey(fromPath)){
								String fromValue = paths.get(fromPath);
								copy.put(fromPath, fromValue); 
							}
							else{
								//Method call that finds elements in XML instance and returns the value
								String elemValue = findElement(elements, fromPath);
								copy.put(fromPath, elemValue);

							}
							mappedElements.add(fromPath);
							fromIt.remove();														
						}

						//After the above code we have all the elements in a many to one rule in copy
						Iterator<Map.Entry<String, String>> mtoIt = copy.entrySet().iterator();
						String fValue = mto.getToRegEx();
						String toXPath = mto.getToXPath();

						while (mtoIt.hasNext()) {
							Map.Entry<String, String> tempPair = (Entry<String, String>) mtoIt.next();
							String key = mto.getFromXPath().get(tempPair.getKey()); //eg. $1								
							String regex = mto.getFromRegEx().get(tempPair.getKey()); //eg. (\d\d):(\d\d)
							List<VarBinding> vbList = mto.getVariableBinding().get(tempPair.getKey()); //Variable bindings

							if(key == null && regex != null && vbList == null){ //if null means that it has regex in the other map + needs to be tokenized
								Map<String, String> tokens = regexParser(regex, tempPair.getValue(), itemSize+1, NORMAL_TOKEN_PARSER);
								Iterator<Map.Entry<String, String>> tokensIt = tokens.entrySet().iterator();

								while (tokensIt.hasNext()) {//iterate through tokens
									Map.Entry<String, String> tempPairToken = (Entry<String, String>) tokensIt.next();

									String regexKey = tempPairToken.getKey(); //eg. $1
									String kValue = tempPairToken.getValue(); //eg. 27 
									fValue = fValue.replace(regexKey, kValue);					        
									tokensIt.remove(); // avoids a ConcurrentModificationException
									itemSize++;
								}										
							}
							else if(key != null) //XPath only elements (without RegEx)
								fValue = fValue.replace(key, tempPair.getValue());									

							//VARIABLE BINDING
							if(vbList != null){
								List<String> tokenValues = null;

								//DO THE MAPPINGS OF VARIABLES
								//i.e: {$a => name, $b => surname}							
								Map<String, String> variableBindings = new HashMap<String, String>();

								//In order to have variable binding REGEX is needed, except Explicit Binding
								if(regex != null){
									Map<String, String> tokens = regexParser(regex, tempPair.getValue(), 1, VARIABLE_BINDING_TOKEN_PARSER);
									tokenValues = new ArrayList<String>(tokens.values());
								}

								for(VarBinding v : vbList){

									//Prefix
									if(v.getExplicitPrefixBinding() != null){
										variableBindings.put(v.getExplicitPrefixBinding().get(0), tokenValues.get(0));
										tokenValues.remove(0);
									}

									//List Binding
									if(v.getListBinding() != null){
										for(String s : v.getListBinding().split(" ")){
											variableBindings.put(s, tokenValues.get(0));
											tokenValues.remove(0);
										}
									}

									//Postfix
									if(v.getExplicitPostfixBinding() != null){
										int lastIndex = tokenValues.size() - 1;
										variableBindings.put(v.getExplicitPostfixBinding().get(0), tokenValues.get(lastIndex));
									}
								}

								// ------- MAPPINGS END -----------------

								for(VarBinding v : vbList){

									//Prefix
									//The first prefix binding in the list is taken (makes more sense)
									if(v.getExplicitPrefixBinding() != null){ 	
										String keyBinding = v.getExplicitPrefixBinding().get(0);											
										if(fValue.contains(keyBinding)){
											fValue = fValue.replace(keyBinding, variableBindings.get(keyBinding));
											itemSize++;
										}
									}

									//ListBinding
									if(v.getListBinding() != null){

										//Check current toField if it contains each regex
										for(String keyBinding : v.getListBinding().split(" ")){ //splits by space eg. [$a, $b]	
											//This is for regex that appear in toXPath
											if(fValue.contains(keyBinding)){
												fValue = fValue.replace(keyBinding, variableBindings.get(keyBinding));
												itemSize++;
											}	
										}
									}

									//Postfix
									//The first Postfix binding in the list is taken (makes more sense)
									if(v.getExplicitPostfixBinding() != null){
										String keyBinding = v.getExplicitPostfixBinding().get(0);
										if(fValue.contains(keyBinding)){											
											fValue = fValue.replace(keyBinding, variableBindings.get(keyBinding));
											itemSize++;
										}
									}

									//Explicit Binding only - does not need reg ex
									else if(v.getExplicitBinding() != null){
										String keyBinding = v.getExplicitBinding();
										if(fValue.contains(keyBinding)){
											String g = tempPair.getValue();
											fValue = fValue.replace(keyBinding, g);
											itemSize++;
										}
									}
								}
							}

							mtoIt.remove(); // avoids a ConcurrentModificationException
						}

						String path[] = toXPath.split("/");
						fillCore(path, fValue, roiElement, jpsC); //At this point fValue contains the final replaced value eliminated 
						//from any token identifiers
					}
					else{
						unknownElements.put(xpath, value);
					}
				}
			}

			//System.out.println("");
			//jpsC.dumpData();

			//Fix the unknown Elements values. Some rules were also added from the ManyToOne
			for(String s : mappedElements){
				if(s.contains("/")){ //nested elements
					String [] path = s.split("/");
					for(String p : path){
						if(unknownElements.containsKey(p))
							unknownElements.remove(p);
					}
				}
				//check whole path
				if(unknownElements.containsKey(s))
					unknownElements.remove(s);
			}

		} 
		catch (IOException io) {
			System.out.println(io.getMessage());
			throw io;
		} 
		catch (JDOMException jdomex) {
			System.out.println(jdomex.getMessage());
			throw jdomex;
		}
		// ----------------------------------


		return jpsC;
	}

	/*
	 * Takes an XPath, finds the correct place in JPS Core and adds the value to it
	 * 
	 * p: XPath to the specific field
	 * value: The actual value to be added to the specific field
	 * elem: If an object has level 2 objects in a list that belong to (e.g. Source.SourceElement)
	 * set: For elements that are lists, specifies the list element they belong to
	 * jpsC: The JPSearch core to be updated/returned
	 * 
	 */
	public static boolean fillCore(String [] p, String value, int roiElement, JPSearchCore jpsC){

		if(p == null || p[0].equals("unknown") || p.length == 1){
			return false;
		}

		int curIndex = 0;
		//skip empty values
		for(; curIndex < p.length; curIndex++){
			if(!p[curIndex].equals(""))
				break;
		}

		if(p[curIndex++].equals("JPSearchCore")){
			String base = p[curIndex++];

			///JPSearchCore/Identifier
			if(base.equals("Identifier")){
				jpsC.setIdentifier(value);
			}

			///JPSearchCore/Creators
			else if(base.equals("Creators")){
				String second = p[curIndex++];
				Person newPerson = null;

				//Get last element added if exists. If it is already filled then 
				//create a new object and add it.
				//If structure empty create a new object and add it to the structure.
				if(jpsC.getCreators().size() > 0) //get Last
					newPerson = jpsC.getCreators().get(jpsC.getCreators().size() - 1);
				else{
					newPerson = new Person();
					jpsC.getCreators().add(newPerson); //POINTERS POWER!!
				}

				if(second.equals("GivenName")){
					if(StringUtils.isBlank(newPerson.getGivenName())){ //check if blank
						newPerson.setGivenName(value);
					}
					else{
						newPerson = new Person();
						newPerson.setGivenName(value);
						jpsC.getCreators().add(newPerson);
					}

				}
				else if(second.equals("FamilyName")){
					if(StringUtils.isBlank(newPerson.getFamilyName())){ //check if blank
						newPerson.setFamilyName(value);
					}
					else{
						newPerson = new Person();
						newPerson.setFamilyName(value);
						jpsC.getCreators().add(newPerson);
					}					
				}
			}

			///JPSearchCore/Modifiers
			if(base.equals("Modifiers")){
				String second = p[curIndex++];
				Person newPerson = null; 
				//Check for existing modifier (if trying to enter family name to an existing modifier)
				//maybe set attribute to store following data to pre-existing data

				//Check if the specific creators already exists and pick his element				
				if(jpsC.getModifiers().size() > 0)
					newPerson = jpsC.getModifiers().get(jpsC.getModifiers().size() - 1);
				else{
					newPerson = new Person();
					jpsC.getModifiers().add(newPerson);	 //POINTERS POWER!!
				}

				if(second.equals("GivenName")){
					if(StringUtils.isBlank(newPerson.getGivenName())){ //check if blank
						newPerson.setGivenName(value);
					}
					else{
						newPerson = new Person();
						newPerson.setGivenName(value);
						jpsC.getModifiers().add(newPerson);
					}					
				}
				else if(second.equals("FamilyName")){
					if(StringUtils.isBlank(newPerson.getFamilyName())){ //check if blank
						newPerson.setFamilyName(value);
					}
					else{
						newPerson = new Person();
						newPerson.setFamilyName(value);
						jpsC.getModifiers().add(newPerson);
					}	
				}						
			}

			///JPSearchCore/Publisher
			else if(base.equals("Publisher")){
				String second = p[curIndex++];
				Publisher newPub = null;

				if(jpsC.getPublishers().size() > 0)
					newPub = jpsC.getPublishers().get(jpsC.getPublishers().size() - 1);
				else{
					newPub = new Publisher();
					jpsC.getPublishers().add(newPub);
				}

				///JPSearchCore/Publisher/PersonName
				if(second.equals("PersonName")){
					String third = p[curIndex++];
					Person newPerson = null; 

					//Person name is of type Person
					if(newPub.getPersonName() != null){
						newPerson = newPub.getPersonName();
					}
					else{
						newPerson = new Person();
						newPub.setPersonName(newPerson);
					}


					if(third.equals("GivenName")){
						if(StringUtils.isBlank(newPerson.getGivenName())){ //check if blank
							newPerson.setGivenName(value);
						}
						else{
							newPerson = new Person();
							newPerson.setGivenName(value);
							newPub.setPersonName(newPerson);
						}		
					}
					else if(third.equals("FamilyName")){
						if(StringUtils.isBlank(newPerson.getFamilyName())){ //check if blank
							newPerson.setFamilyName(value);
						}
						else{
							newPerson = new Person();
							newPerson.setFamilyName(value);
							newPub.setPersonName(newPerson);
						}	
					}		
				}

				///JPSearchCore/Publisher/OrganizationInformation
				else if(second.equals("OrganizationInformation")){
					String third = p[curIndex++];

					CDOrganizationType cdo = null;
					if(newPub.getOrgInfo() != null){
						cdo = newPub.getOrgInfo();
					}
					else{
						cdo = new CDOrganizationType();
						newPub.setOrgInfo(cdo);
					}

					///JPSearchCore/Publisher/OrganizationInformation/Name
					if(third.equals("Name")){
						if(cdo.getNames().isEmpty())
							cdo.getNames().add(value); //left like that because it is a list and if a name comes up again
						//it is just added to the list
						else{
							cdo = new CDOrganizationType();
							cdo.getNames().add(value);
							newPub.setOrgInfo(cdo);
						}
					}

					///JPSearchCore/Publisher/OrganizationInformation/Address
					else if(third.equals("Address")){
						String fourth = p[curIndex++];

						Place newPlace = null;

						if(cdo.getAddress() != null)
							newPlace = cdo.getAddress();
						else{
							newPlace = new Place();
							cdo.setAddress(newPlace);
						}

						///JPSearchCore/Publisher/OrganizationInformation/Address/Name
						if(fourth.equals("Name")){
							newPlace.getName().add(value);
						}

						///JPSearchCore/Publisher/OrganizationInformation/Address/Description
						else if(fourth.equals("Description")){
							newPlace.getDescription().add(value);
						}					
					}
				}
			}

			//JPSearchCore/CreationDate
			else if(base.equals("CreationDate")){
				jpsC.setCreationDate(value);
			}

			//JPSearchCore/ModifiedDate
			else if(base.equals("ModifiedDate")){
				jpsC.setModifiedDate(value);
			}

			//JPSearchCore/Description
			else if(base.equals("Description")){
				String currentValue = jpsC.getDescription();
				if(currentValue == null)
					jpsC.setDescription(value);
				else
					jpsC.setDescription(currentValue + " " + value);
			}

			//Rights Description
			else if(base.equals("RightsDescription")){
				String second = p[curIndex++];

				RightsDescription rd = null;
				if(jpsC.getRightsDescription() == null){
					rd = new RightsDescription();
					jpsC.setRightsDescription(rd);
				}
				else
					rd = jpsC.getRightsDescription();

				//JPSearchCore/Rights Description/RightsDescriptionInformation
				if(second.equals("RightsDescriptionInformation")){
					rd.setRightsDescriptionInformation(value);
				}

				//JPSearchCore/Rights Description/Description
				else if(second.equals("Description")){
					rd.setDescription(value);
				}

				//JPSearchCore/Rights Description/ActualRightsDescriptionReference
				else if(second.equals("ActualRightsDescriptionReference")){
					rd.setActualRightsDescriptionReference(value);
				}

				//JPSearchCore/Rights Description/ActualRightsDescription
				else if(second.equals("ActualRightsDescription")){
					rd.setActualRightsDescription(value);
				}				
			}

			//Source
			else if(base.equals("Source")){
				String second = p[curIndex++];
				Source newS = null;
				if(jpsC.getSource().size() > 0){
					newS = jpsC.getSource().get(jpsC.getSource().size() - 1);
				}
				else{
					newS = new Source();
					jpsC.getSource().add(newS);
				}

				//JPSearchCore/Source/SourceElementType
				if(second.equals("SourceElementType")){
					if(StringUtils.isBlank(newS.getSourceElementType())){
						newS.setSourceElementType(value);
					}
					else{
						newS = new Source();
						newS.setSourceElementType(value);
						jpsC.getSource().add(newS);
					}					
				}

				//JPSearchCore/Source/SourceElement
				else if(second.equals("SourceElement")){
					String third = p[curIndex++];

					SourceElement newSE = null;
					if(newS.getSourceElements().size() > 0){
						newSE = newS.getSourceElements().get(newS.getSourceElements().size() - 1);
					}
					else{
						newSE = new SourceElement();
						newS.getSourceElements().add(newSE);
					}

					//JPSearchCore/Source/SourceElement/SourceElementTitle
					if(third.equals("SourceElementTitle")){
						if(StringUtils.isBlank(newSE.getSourceElementTitle())){
							newSE.setSourceElementTitle(value);
						}
						else{
							newSE = new SourceElement();
							newSE.setSourceElementTitle(value);
							newS.getSourceElements().add(newSE);
						}						
					}

					//JPSearchCore/Source/SourceElement/SourceElementDescription
					else if(third.equals("SourceElementDescription")){
						if(StringUtils.isBlank(newSE.getSourceElementDescription())){
							newSE.setSourceElementDescription(value);
						}
						else{
							newSE = new SourceElement();
							newSE.setSourceElementDescription(value);
							newS.getSourceElements().add(newSE);
						}						
					}

					//JPSearchCore/Source/SourceElement/SourceElementIdentifier
					else if(third.equals("SourceElementIdentifier")){
						if(StringUtils.isBlank(newSE.getSourceElementIdentifier())){
							newSE.setSourceElementIdentifier(value);
						}
						else{
							newSE = new SourceElement();
							newSE.setSourceElementIdentifier(value);
							newS.getSourceElements().add(newSE);
						}	
					}		
				}

				//JPSearchCore/Source/CreationMethod
				else if(second.equals("CreationMethod")){					
					if(StringUtils.isBlank(newS.getCreationMethod())){
						newS.setCreationMethod(value);
					}
					else{
						newS = new Source();
						newS.setCreationMethod(value);
						jpsC.getSource().add(newS);
					}	
				}

				//JPSearchCore/Source/CreationDescription
				else if(second.equals("CreationDescription")){
					if(StringUtils.isBlank(newS.getCreationDescription())){
						newS.setCreationDescription(value);
					}
					else{
						newS = new Source();
						newS.setCreationDescription(value);
						jpsC.getSource().add(newS);
					}
				}
			}

			///JPSearchCore/Keyword
			else if(base.equals("Keyword")){
				jpsC.getKeywords().add(value);
			}

			// /JPSearchCore/Title
			else if(base.equals("Title")){
				jpsC.setTitle(value);
			}

			//CollectionLabel section
			else if(base.equals("CollectionLabel")){
				jpsC.getCollectionLabel().add(value);
			}

			// /JPSearchCore/PreferredValue
			else if(base.equals("PreferredValue")){
				jpsC.setPreferredValue(Integer.parseInt(value));
			}

			// /JPSearchCore/Rating
			else if(base.equals("Rating")){
				String second = p[curIndex++];

				Rating rat = null;
				if(jpsC.getRating() == null){
					rat = new Rating();
					jpsC.setRating(rat);
				}
				else
					rat = jpsC.getRating();						

				// /JPSearchCore/Rating/LabelDefinition
				if(second.equals("LabelDefinition")){
					rat.setLabelDefinition(value);
				}

				// /JPSearchCore/Rating/LabelValue
				else if(second.equals("LabelValue")){
					rat.setLabelValue(value);
				}				
			}

			//OriginalImageIdentifier section
			else if(base.equals("OriginalImageIdentifier")){
				String second = p[curIndex++];

				OriginalImageIdentifier oii = null;
				if(jpsC.getOriginalImageIdentifier() == null){
					oii = new OriginalImageIdentifier();
					jpsC.setOriginalImageIdentifier(oii);
				}
				else
					oii = jpsC.getOriginalImageIdentifier();

				// /JPSearchCore/OriginalImageIdentifier/Identifier
				if(second.equals("Identifier")){
					oii.setIdentifier(value);
				}

				// /JPSearchCore/OriginalImageIdentifier/OriginationOfID
				else if(second.equals("OriginationOfID")){
					oii.setOriginationofID(value);
				}
			}

			// /JPSearchCore/GPSPositioning
			else if(base.equals("GPSPositioning")){
				String second = p[curIndex++];
				GPSPositioning newGPS = jpsC.getGpsPositioning();
				if(newGPS == null)
					newGPS = new GPSPositioning(0,0,0);

				// /JPSearchCore/GPSPositioning/longitude
				if(second.equals("longitude")){
					newGPS.setLongitude(Double.parseDouble(value));
				}

				// /JPSearchCore/GPSPositioning/latitude
				else if(second.equals("latitude")){
					newGPS.setLatitude(Double.parseDouble(value));
				}

				// /JPSearchCore/GPSPositioning/altitude
				else if(second.equals("altitude")){
					newGPS.setAltitude(Double.parseDouble(value));
				}

				jpsC.setGpsPositioning(newGPS);
			}

			//Region of interest section
			//An identifier must specify the updated region of interest
			else if(base.equals("RegionOfInterest")){
				String second = p[curIndex++];

				RegionOfInterest newRoi = null;
				if(jpsC.getRegionOfInterest().size() > 0 && jpsC.getRegionOfInterest().size() > roiElement){
					newRoi = jpsC.getRegionOfInterest().get(roiElement);
				}
				else{
					newRoi = new RegionOfInterest();
					newRoi.setTitle("[New Region of Interest]");
					jpsC.getRegionOfInterest().add(newRoi);
				}				

				// /JPSearchCore/RegionOfInterest/RegionLocator (set??????)
				if(second.equals("RegionLocator")){
					RegionLocator newRL = null;

					if(newRoi.getRegionLocator() != null){
						newRL = newRoi.getRegionLocator();
					}
					else{
						newRL = new RegionLocator();
						newRoi.setRegionLocator(newRL);
					}

					String area[] = value.split(" ");
					List<Integer> region = new ArrayList<Integer>();
					for(String s : area){
						region.add(Integer.parseInt(s));
					}

					newRL.getRegions().add(new Region(region, region.size()/2));					
				}

				// /JPSearchCore/RegionOfInterest/Description
				else if(second.equals("Description")){
					newRoi.setDescription(value);
				}

				// /JPSearchCore/RegionOfInterest/Keyword
				else if(second.equals("Keyword")){
					newRoi.getKeywords().add(value);
				}

				// /JPSearchCore/RegionOfInterest/Title
				else if(second.equals("Title")){
					newRoi.setTitle(value);
				}

				// /JPSearchCore/RegionOfInterest/ContentDescription
				else if(second.equals("ContentDescription")){
					String third = p[curIndex++];

					ContentDesciption newCD = newRoi.getContentDescription();				

					// /JPSearchCore/RegionOfInterest/ContentDescription/Person
					if(third.equals("Person")){						
						String fourth = p[curIndex++];

						CDPerson newPerson = null;
						if(newCD.getPerson().size() > 0){
							newPerson = newCD.getPerson().get(newCD.getPerson().size() - 1);
						}
						else{
							newPerson = new CDPerson();
							newCD.getPerson().add(newPerson);
						}						

						// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Name
						if(fourth.equals("Name")){
							String fifth = p[curIndex++];

							Person newP;
							if(newPerson.getNames() != null){
								newP = newPerson.getNames();
							}
							else{
								newP = new Person(); 
								newPerson.setNames(newP);
							}

							if(fifth.equals("GivenName")){
								if(StringUtils.isBlank(newP.getGivenName())){ //check if blank
									newPerson.setGivenName(value);
								}
								else{
									newP = new Person();
									newP.setGivenName(value);
									newPerson.setNames(newP);
								}		
							}
							else if(fifth.equals("FamilyName")){
								if(StringUtils.isBlank(newP.getFamilyName())){ //check if blank
									newP.setFamilyName(value);
								}
								else{
									newP = new Person();
									newP.setFamilyName(value);
									newPerson.setNames(newP);
								}	
							}
						}

						// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Affiliation
						else if(fourth.equals("Affiliation")){

							String fifth = p[curIndex++];

							CDOrganizationType newOrgType; //if an affiliation already added
							if(newPerson.getAffiliation().size() > 0){
								newOrgType = newPerson.getAffiliation().get(newPerson.getAffiliation().size() - 1);
							}
							else{
								newOrgType = new CDOrganizationType();
								newPerson.getAffiliation().add(newOrgType);
							}	

							// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Affiliation/Name
							if(fifth.equals("Name")){
								if(newOrgType.getNames().isEmpty())
									newOrgType.getNames().add(value);
								else{
									newOrgType = new CDOrganizationType();
									newOrgType.getNames().add(value);
									newPerson.getAffiliation().add(newOrgType);
								}
							}

							// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Affiliation/Address
							else if(fifth.equals("Address")){
								String sixth = p[curIndex++];

								if(newOrgType.getAddress() == null) newOrgType.setAddress(new Place());

								// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Affiliation/Address/Name
								if(sixth.equals("Name")){
									if(newOrgType.getAddress().getName().isEmpty())
										newOrgType.getAddress().getName().add(value);
									else{
										newOrgType = new CDOrganizationType();
										newOrgType.getNames().add("Name");
										newOrgType.getAddress().getName().add(value);
										newPerson.getAffiliation().add(newOrgType);
									}
								}

								// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Affiliation/Address/Description
								else if(sixth.equals("Description")){
									if(newOrgType.getAddress().getDescription().isEmpty())
										newOrgType.getAddress().getDescription().add(value);
									else{
										newOrgType = new CDOrganizationType();
										newOrgType.getNames().add("Name");
										newOrgType.getAddress().getName().add("Name");
										newOrgType.getAddress().getDescription().add(value);
										newPerson.getAffiliation().add(newOrgType);
									}
								}
							}
						}

						// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Address
						else if(fourth.equals("Address")){
							String fifth = p[curIndex++];

							if(newPerson.getAddress() == null) newPerson.setAddress(new Place());

							// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Address/Name
							if(fifth.equals("Name")){
								if(newPerson.getAddress().getName().isEmpty())
									newPerson.getAddress().getName().add(value);
								else{
									newPerson = new CDPerson();
									newPerson.setGivenName("GivenName");
									newPerson.setFamilyName("FamilyName");
									newPerson.setAddress(new Place());
									newPerson.getAddress().getName().add(value);
									newCD.getPerson().add(newPerson); 
								}
							}

							// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Address/Description
							else if(fifth.equals("Description")){								
								if(newPerson.getAddress().getName().isEmpty() == false)
									newPerson.getAddress().getDescription().add(value);
								else{
									newPerson = new CDPerson();
									newPerson.setGivenName("GivenName");
									newPerson.setFamilyName("FamilyName");
									newPerson.setAddress(new Place());
									newPerson.getAddress().getName().add("Name");
									newPerson.getAddress().getDescription().add(value);
									newCD.getPerson().add(newPerson); 
								}
							}
						}

						// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Description
						else if(fourth.equals("Description")){
							if(StringUtils.isBlank(newPerson.getDescription())){
								newPerson.setDescription(value);
							}
							else{
								newPerson = new CDPerson();
								newPerson.setDescription(value);
								newCD.getPerson().add(newPerson);
							}

						}
						// /JPSearchCore/RegionOfInterest/ContentDescription/Person/Nationality
						else if(fourth.equals("Nationality")){
							if(StringUtils.isBlank(newPerson.getNationality())){
								newPerson.setNationality(value);
							}
							else{
								newPerson = new CDPerson();
								newPerson.setNationality(value);
								newCD.getPerson().add(newPerson);
							}
						}
					}

					// /JPSearchCore/RegionOfInterest/ContentDescription/Object
					else if(third.equals("Object")){
						String fourth = p[curIndex++];

						CDObject newObject = null;
						if(newCD.getObject().size() > 0){
							newObject = newCD.getObject().get(newCD.getObject().size() - 1);
						}
						else{
							newObject = new CDObject();
							newCD.getObject().add(newObject);
						}	

						// /JPSearchCore/RegionOfInterest/ContentDescription/Object/Name
						if(fourth.equals("Name")){
							if(newObject.getName().isEmpty())
								newObject.getName().add(value);
							else{
								newObject = new CDObject();
								newObject.getName().add(value);
								newCD.getObject().add(newObject);
							}
						}

						///JPSearchCore/RegionOfInterest/ContentDescription/Object/Description
						else if(fourth.equals("Description")){							
							if(StringUtils.isBlank(newObject.getDescription()))
								newObject.setDescription(value);
							else{
								newObject = new CDObject();
								newObject.getName().add("Name");
								newObject.setDescription(value);
								newCD.getObject().add(newObject);
							}
						}

						///JPSearchCore/RegionOfInterest/ContentDescription/Object/Label
						else if(fourth.equals("Label")){

							if(StringUtils.isBlank(newObject.getLabel()))
								newObject.setLabel(value);
							else{
								newObject = new CDObject();
								newObject.getName().add("Name");
								newObject.setLabel(value);
								newCD.getObject().add(newObject);
							}
						}
					}

					// /JPSearchCore/RegionOfInterest/ContentDescription/Place
					else if(third.equals("Place")){
						String fourth = p[curIndex++];

						Place newPlace = null;

						if(newCD.getPlace().size() > 0){
							newPlace = newCD.getPlace().get(newCD.getPlace().size() - 1);
						}
						else{
							newPlace = new Place();
							newCD.getPlace().add(newPlace);
						}	

						// /JPSearchCore/RegionOfInterest/ContentDescription/Place/Name
						if(fourth.equals("Name")){
							if(newPlace.getName().isEmpty())
								newPlace.getName().add(value);
							else{
								newPlace = new Place();								
								newPlace.getName().add(value);
								newCD.getPlace().add(newPlace);
							}
						}

						// /JPSearchCore/RegionOfInterest/ContentDescription/Place/Description
						else if(fourth.equals("Description")){								
							if(newPlace.getDescription().isEmpty())
								newPlace.getDescription().add(value);
							else{
								newPlace = new Place();								
								newPlace.getName().add("Name");
								newPlace.getDescription().add(value);
								newCD.getPlace().add(newPlace);
							}
						}
					}

					// /JPSearchCore/RegionOfInterest/ContentDescription/Event
					else if(third.equals("Event")){
						String fourth = p[curIndex++];

						CDEvent newEvent = null;

						if(newCD.getEvent().size() > 0){
							newEvent = newCD.getEvent().get(newCD.getEvent().size() - 1);
						}
						else{
							newEvent = new CDEvent();
							newCD.getEvent().add(newEvent);
						}	

						// /JPSearchCore/RegionOfInterest/ContentDescription/Event/Label
						if(fourth.equals("Label")){
							if(newEvent.getLabel().isEmpty())
								newEvent.getLabel().add(value);
							else{
								newEvent = new CDEvent();
								newEvent.getLabel().add(value);
								newCD.getEvent().add(newEvent);
							}
						}

						// /JPSearchCore/RegionOfInterest/ContentDescription/Event/Description
						else if(fourth.equals("Description")){					
							if(newEvent.getDescription().isEmpty())
								newEvent.getDescription().add(value);
							else{
								newEvent = new CDEvent();
								newEvent.getLabel().add("Label");
								newEvent.getDescription().add(value);
								newCD.getEvent().add(newEvent);
							}
						}
					}

					newRoi.setContentDescription(newCD);

				}//END Content Description

				// /JPSearchCore/RegionOfInterest/ExternalDescription
				else if(second.equals("ExternalDescription")){
					String third = p[curIndex++];

					ExternalDescription newED;

					if(newRoi.getExternalDescription().size() > 0){
						newED = newRoi.getExternalDescription().get(newRoi.getExternalDescription().size() - 1);
					}
					else{
						newED = new ExternalDescription();
						newRoi.getExternalDescription().add(newED);
					}

					// /JPSearchCore/RegionOfInterest/ExternalDescription/TagName
					if(third.equals("TagName")){

						EDTagName newEDTN;
						if(newED.getTagName() != null)
							newEDTN = newED.getTagName();
						else{
							newEDTN = new EDTagName();
							newED.setTagName(newEDTN);
						}

						if(curIndex + 1 < p.length){ //if XPath continues to the attribute fields
							String fourth = p[curIndex++];						

							// /JPSearchCore/RegionOfInterest/ExternalDescription/TagName/fromNamespace
							if(fourth.equals("fromNamespace")){
								newEDTN.setFromNameSpace(value);
							}

							// /JPSearchCore/RegionOfInterest/ExternalDescription/TagName/fromNamespacePrefix
							else if(fourth.equals("fromNamespacePrefix")){
								newEDTN.setFromNameSpacePrefix(value);
							}
						}
						else{
							newEDTN.setContent(value);
						}						
					}

					// /JPSearchCore/RegionOfInterest/ExternalDescription/LiteralValue
					else if(third.equals("LiteralValue")){

						EDLiteralValue newEDLV;
						if(newED.getLiteralValue() != null)
							newEDLV = newED.getLiteralValue();
						else{
							newEDLV = new EDLiteralValue();
							newED.setLiteralValue(newEDLV);
						}

						if(curIndex + 1 < p.length){
							String fourth = p[curIndex++];

							// /JPSearchCore/RegionOfInterest/ExternalDescription/LiteralValue/fromControlledVocabularyOfLiterals
							if(fourth.equals("fromControlledVocabularyOfLiterals")){
								newEDLV.setFromControlledVocabularyOfLiterals(value);
							}

							// /JPSearchCore/RegionOfInterest/ExternalDescription/LiteralValue/fromGrammarOfLiterals
							else if(fourth.equals("fromGrammarOfLiterals")){
								newEDLV.setFromGrammarOfLiterals(value);
							}
						}
						else{
							newEDLV.setContent(value);
						}
					}

					// /JPSearchCore/RegionOfInterest/ExternalDescription/ReourceValue
					else if(third.equals("ResourceValue")){

						EDResourceValue newEDRV;
						if(newED.getResourceValue() != null)
							newEDRV = newED.getResourceValue();
						else{
							newEDRV = new EDResourceValue();
							newED.setResourceValue(newEDRV);
						}

						if(curIndex + 1 < p.length){
							String fourth = p[curIndex++];

							// /JPSearchCore/RegionOfInterest/ExternalDescription/ResourceValue/fromNamespace
							if(fourth.equals("fromNamespace")){
								newEDRV.setFromNameSpace(value);
							}							
						}
						else{
							newEDRV.setContent(value);
						}
					}

					// /JPSearchCore/RegionOfInterest/ExternalDescription/StructuredValue
					else if(third.equals("StructuredValue")){

						EDStructuredValue newEDSV;
						if(newED.getStructuredValue () != null)
							newEDSV = newED.getStructuredValue();
						else{
							newEDSV = new EDStructuredValue();
							newED.setStructuredValue(newEDSV);
						}

						if(curIndex + 1 < p.length){
							String fourth = p[curIndex++];

							// /JPSearchCore/RegionOfInterest/ExternalDescription/StructuredValue/fromNamespace
							if(fourth.equals("fromNamespace")){
								newEDSV.setFromNameSpace(value);
							}							
						}
						else{//content
							newEDSV.getContent().add(value);
						}
					}
				}
			}	

			///JPSearchCore/Width
			else if(base.equals("Width")){
				jpsC.setWidth(Integer.parseInt(value));
			}

			///JPSearchCore/Height
			else if(base.equals("Height")){
				jpsC.setHeight(Integer.parseInt(value));
			}
		}

		return true;
	}

	public static void pathCreator(Element elementsLeft, List<String> allPaths){			
		for(Element e : elementsLeft.getChildren()){
			String pathSoFar = "/" + elementsLeft.getName();
			pathCreator(pathSoFar, e, allPaths);
		}
	}

	public static void pathCreator(String pathSoFar, Element elementsLeft, List<String> allPaths){

		pathSoFar += "/" + elementsLeft.getName(); 

		for(Element e : elementsLeft.getChildren()){
			pathCreator(pathSoFar, e, allPaths);
		}

		if(StringUtils.isBlank(elementsLeft.getTextTrim()) == false)
			allPaths.add(pathSoFar);
		return;
	}

	/**
	 * Creates an XPath like of the XML structure 
	 * @param elementsLeft The elements left to be processed
	 * @param allPaths The map containing all the added paths
	 * @param attr The map containing the Region of Interest attribute value for each path
	 */
	public static void pathCreator( Element elementsLeft, 
			Map<String,String> allPaths,
			Map<String,Integer> attr){	

		//If element has NO children
		if(elementsLeft.getChildren().isEmpty()){

			String path = "/" + elementsLeft.getName();
			String copyPath = "/" + elementsLeft.getName();
			if(elementsLeft.getAttributes().isEmpty() == false){				
				path += "[";
				for(int i = 0; i<elementsLeft.getAttributes().size(); i++){
					Attribute a = elementsLeft.getAttributes().get(i);
					if(a.getName().equals("roiid") == false)
						path += "@" + a.getName() + "=\"" + a.getValue() + "\"";
					if(i+1 < elementsLeft.getAttributes().size())
						path += " and ";
				}
				path += "]";
			}	

			allPaths.put(path, elementsLeft.getTextTrim());

			if(elementsLeft.getAttributes().isEmpty() == false){
				for(Attribute a : elementsLeft.getAttributes()){
					copyPath += "/@" + a.getName();
					allPaths.put(copyPath, a.getValue());
				}
			}

			if(elementsLeft.getAttribute("roiid") != null)
				try {
					attr.put(path, elementsLeft.getAttribute("roiid").getIntValue());
				} catch (DataConversionException e1) {
					attr.put(path, 0);
				}
			else
				attr.put(path, 0); //Set to 0 to avoid ArrayIndex out of bounds

			return;
		}

		//Elements CONTAINS children
		//write attributes before creating path
		String pathSoFar = "/" + elementsLeft.getName();

		if(elementsLeft.getAttributes().isEmpty() == false){
			pathSoFar += "[";
			for(int i = 0; i<elementsLeft.getAttributes().size(); i++){
				Attribute a = elementsLeft.getAttributes().get(i);
				if(a.getName().equals("roiid") == false)
					pathSoFar += "@" + a.getName() + "=\"" + a.getValue() + "\"";
				if(i+1 < elementsLeft.getAttributes().size())
					pathSoFar += " and ";
			}
			pathSoFar += "]";

			//Taking care of attributes 
			if(elementsLeft.getAttributes().isEmpty() == false){
				for(Attribute a : elementsLeft.getAttributes()){
					if(a.getName().equals("roiid") == false){
						String copyPath = "/" + elementsLeft.getName() + "/@" + a.getName();;
						allPaths.put(copyPath, a.getValue());
						attr.put(copyPath, 0);
					}
				}
			}
		}

		for(Element e : elementsLeft.getChildren()){			
			pathCreator(pathSoFar, e, allPaths, attr);
		}
	}

	public static void pathCreator(String pathSoFar,
			Element elementsLeft,
			Map<String,String> allPaths, 
			Map<String,Integer> attr){		

		String currentPath = pathSoFar + "/" + elementsLeft.getName();

		if(elementsLeft.getAttributes().isEmpty() == false){
			currentPath += "[";
			for(int i = 0; i<elementsLeft.getAttributes().size(); i++){
				Attribute a = elementsLeft.getAttributes().get(i);
				if(a.getName().equals("roiid") == false)
					currentPath += "@" + a.getName() + "=\"" + a.getValue() + "\"";
				if(i+1 < elementsLeft.getAttributes().size())
					currentPath += " and ";
			}
			currentPath += "]";

			//Taking care of attributes. 
			//TODO: if multiple elements exists with the same attribute what should it do? Add them all together (concatenation)
			if(elementsLeft.getAttributes().isEmpty() == false){
				for(Attribute a : elementsLeft.getAttributes()){
					if(a.getName().equals("roiid") == false){
						String copyPath = pathSoFar + "/" + elementsLeft.getName() + "/@" + a.getName();
						allPaths.put(copyPath, a.getValue());
						attr.put(copyPath, 0);
					}
				}
			}
		}

		for(Element e : elementsLeft.getChildren()){			
			pathCreator(currentPath, e, allPaths, attr);
		}

		if(StringUtils.isBlank(elementsLeft.getTextTrim()) == false){

			allPaths.put(currentPath, elementsLeft.getTextTrim());

			//Handle attributes
			if(elementsLeft.getAttributes().isEmpty() == false){
				for(Attribute a : elementsLeft.getAttributes()){						
					if(a.getName().equals("roiid") == false){
						String copyPath = pathSoFar + "/" + elementsLeft.getName() + "/@" + a.getName();
						allPaths.put(copyPath, a.getValue());
						attr.put(copyPath, 0);
					}
				}
			}

			try {
				int roiElement = 0; // avoid index out of bounds exception

				if(elementsLeft.getAttribute("roiid") != null)
					roiElement = elementsLeft.getAttribute("roiid").getIntValue();	

				attr.put(currentPath, roiElement);

			} catch (DataConversionException e1) {
				e1.printStackTrace();				
			}			
		}
		return;
	}

	public static Map<String, String> regexParser(String regex, String input, int staringNo, int type){
		Map<String, String> tokens = new LinkedHashMap<String, String>();
		Pattern p = Pattern.compile(regex);
		Matcher m = p.matcher(input);

		if(m.find()){//tokenize $1 => input element, $2 => input..

			if(m.groupCount() > 0){
				for(int i = 1; i <= m.groupCount(); i++){
					if(type == NORMAL_TOKEN_PARSER)
						tokens.put("$" + staringNo, m.group(i));
					else if(type == VARIABLE_BINDING_TOKEN_PARSER)
						tokens.put("$" + Character.toString((char)(staringNo + 96)), m.group(i)); //$a, $b
					staringNo++;
				}	
			}
			else{
				tokens.put("$var", input.substring(m.regionStart(), m.regionEnd()));
			}

			return tokens;
		}
		return null;
	}

	/*
	 * Searches the elements from XML file for a specific element and returns its value 
	 */
	public static String findElement(List<Element> list, String element){
		for(Element e : list){

			//If nested elements 
			if(e.getChildren().size() >= 1){
				Map<String, String> paths = new LinkedHashMap<String, String>();
				Map<String,Integer> attibutes = new LinkedHashMap<String,Integer>();
				pathCreator(e, paths, attibutes);	

				Iterator<Map.Entry<String, String>> pathIt = paths.entrySet().iterator();									

				while(pathIt.hasNext()){
					Map.Entry<String, String> pathPair = (Entry<String, String>) pathIt.next();	
					String xpath = pathPair.getKey();
					if(xpath.equals(element)){
						return pathPair.getValue();
					}					
				}				
			}

			else if(e.getName().equals(element)){
				return e.getValue();
			}
		}

		return null;
	}
}
